if (!require(CDR)){
if (!require(remotes)) {install.packages("remotes")}
remotes::install_github("cdr-book/CDR")
}¿Cómo y dónde se producen los accidentes en Madrid?
Los datos que se utilizan en esta historia están disponibles en el paquete CDR que puede instalarse con el siguiente comando (se comprueba si no lo está):
Accidentes de tráfico en la Ciudad de Madrid registrados por Policía Municipal con víctimas y/o daños al patrimonio en el 2020. Los datos se han descargado del Portal de datos abiertos del Ayuntamiento de Madrid.)
1 Cómo y dónde se producen los accidentes en Madrid
Esta mañana estaba tan tranquilo en mi oficina (trabajo en el distrito de Chamartín para el Ayuntamiento) y me sueltan de repente: “el jefe quiere verte”. Lo primero que he pensado es que me echaban, como soy un poco “cansa almas”… Pero no, entro en el despacho del jefe… ¡Y allí está Almeida! Que ha oído que he hecho un curso de visualización en la UCLM y me he convertido en un crack de la visualización. Yo no sabía donde meterme, pero he mantenido perfectamente la compostura, como un profesional.
Así que ya me ha ido contando que está muy preocupado con los accidentes y quiere analizar el tema, para buscar soluciones. Que me agradecería mucho si encontramos dónde está el problema, para bajar el número y gravedad de los accidentes en la ciudad. El problema, me dice, es que solo me puede facilitar los datos del año 2020. Ya le he avisado de que es un año muy particular, por el estado de alarma, pero algo sacaremos…
2 Entender el contexto
¿Qué le interesa al alcalde? Bajar la tasa de accidentes. Pero desde el punto de vista de la visualización, está claro que va a necesitar información georreferenciada, por lo que haremos mapas. Por otro lado, seguro que la dimensión es importante, porque no es lo mismo Madrid en agosto que un día de lluvia en calendario escolar…
En cualquier caso, lo primero es hacer un análisis exploratorio para ver si hay algo raro en los datos y analizar mínimamente las variables de interés. Y después, le vamos a preparar los gráficos necesarios para aportarle conocimiento sobre los accidentes en la ciudad.
library(CDR) # Carga el paquete CDR para la lectura de datos
library(tidyverse) # Manipulación y visualización de datos
library(sf) # Para trabajar con datos espaciales
library(mapSpain) # Para obtener y visualizar mapas de España
library(tidyterra) # Para trabajar con datos espaciales y raster
library(lubridate) # Manipulación de fechas y horas
library(data.table) # Para manipulación eficiente de datos
library(gplots) # Carga el paquete gplots para crear gráficos avanzados
library(RColorBrewer) # Carga el paquete RColorBrewer para paletas de colores
library(leaflet) # Carga el paquete leaflet para crear mapas interactivos
library(leaflet.extras) # Carga el paquete leaflet.extras para funcionalidades adicionales en leaflet
library(ggmosaic) # Carga el paquete ggmosaic para crear gráficos de mosaico3 Elegir una visualización adecuada
3.1 Almeida quiere saber cómo y dónde se producen los accidentes en Madrid
Ejercicio 1 ¿Dónde se han producido los accidentes de tráfico en Madrid en el 2020?
num_expediente fecha hora
1: 2019S040008 07/09/2020 23:00:00
2: 2019S040008 07/09/2020 23:00:00
3: 2020S000001 01/01/2020 1:15:00
4: 2020S000001 01/01/2020 1:15:00
5: 2020S000002 01/01/2020 1:20:00
6: 2020S000002 01/01/2020 1:20:00
localizacion numero distrito
1: CALL. SAN MAXIMILIANO, 38 38 CIUDAD LINEAL
2: CALL. SAN MAXIMILIANO, 38 38 CIUDAD LINEAL
3: AVDA. CANILLEJAS A VICALVARO / CALL. SILFIDE 1 SAN BLAS-CANILLEJAS
4: AVDA. CANILLEJAS A VICALVARO / CALL. SILFIDE 1 SAN BLAS-CANILLEJAS
5: CALL. SILVANO, 31 31 HORTALEZA
6: CALL. SILVANO, 31 31 HORTALEZA
tipo_accidente estado_meteorológico tipo_vehiculo tipo_persona
1: Choque contra obstáculo fijo Despejado Turismo Conductor
2: Choque contra obstáculo fijo Despejado VMU eléctrico Conductor
3: Colisión fronto-lateral NULL Turismo Conductor
4: Colisión fronto-lateral NULL Turismo Conductor
5: Choque contra obstáculo fijo Despejado Turismo Conductor
6: Choque contra obstáculo fijo Despejado Turismo Pasajero
rango_edad sexo lesividad coordenada_x_utm coordenada_y_utm
1: De 21 a 24 años Hombre NULL 444597.8 4475156
2: De 25 a 29 años Mujer NULL 444597.8 4475156
3: De 18 a 20 años Hombre NULL 447710.6 4477156
4: De 30 a 34 años Hombre NULL 447710.6 4477156
5: De 21 a 24 años Hombre NULL 445076.3 4478372
6: De 40 a 44 años Hombre NULL 445076.3 4478372
positiva_alcohol positiva_droga
1: N NULL
2: N NULL
3: N NULL
4: S NULL
5: N NULL
6: N NULL
ggplot(data = accidentes2020_data, # Crea un gráfico con el dataset accidentes2020_data
aes(x = coordenada_x_utm, y = coordenada_y_utm)) + # x= coordenada_x_utm e y= coordenada_y_utm
geom_point() + # Añade puntos al gráfico para cada observación
coord_fixed() # Mantiene una relación fija entre las unidades del eje x y el eje y3.2 El desorden es tu enemigo
Deja mucho que desear, ¿verdad?: no tenemos información, solo unos datos representados en un gráfico. Con muy poco, tirando de estética (reduciendo la dimensión de los puntos para que se vea el fondo) y alguna referencia geográfica (en forma de mapa de carreteras), veremos cómo se reduce la carga cognitiva:
3.2.1 Paso 1: # Convierte el dataset accidentes2020_data a un objeto sf
3.2.2 Paso 2: Obtiene datos del municipio de Madrid con la función esp_get_munic de la libreíra mapSpain
madrid <- esp_get_munic(munic = "^Madrid$") |> # Obtiene los datos del municipio de Madrid
st_transform(25830) # Transforma las coordenadas al sistema de referencia ETRS89/UTM zona 30N
# Descarga una imagen de un mapa estático de las carreteras de Madrid
tile <- esp_getTiles(madrid, "IDErioja", zoommin = 2)3.2.3 Paso 3: Primer mapa mejorado
ggplot() + # Inicia un gráfico vacío con ggplot
geom_spatraster_rgb(data = tile) + # Añade la imagen del mapa estático como fondo
geom_sf(data = accidentes2020_sf, # Añade los datos de accidentes como puntos en el mapa
col = "blue", size = 0.05, alpha = 0.3) + # Define el color, tamaño y transparencia de los puntos
coord_sf(expand = FALSE) + # Mantiene las proporciones de las coordenadas sin expansión
labs(title = "¿Dónde se producen los accidentes de tráfico en Madrid?") ¡Esto ya es otra cosa! Apreciamos, lógicamente, la concentración de accidentes en el centro de la ciudad y, especialmente, en las arterias principales.
3.3 El cuándo
Además, como hemos comentado, tenemos conocimiento a priori sobre la influencia de la fecha en el aumento de tráfico, por lo que probablemente encontremos alguna influencia de la dimensión temporal. Vamos a verlo…
Ejercicio 2 ¿Cuándo se han producido los accidentes?
ggplot(data = accidentes2020_data,
aes(x = fecha, y = hora)) +
geom_point() +
theme_minimal()3.4 Eliminemos de nuevo el desorden
Vamos a separar la fecha y la hora, porque en ese gráfico no vemos casi nada…
3.4.1 Paso 1: Prepara los datos teniendo en cuenta la timensión temporal
# Convertir la columna 'fecha' a formato Date
accidentes2020_data$fecha <- as.Date(accidentes2020_data$fecha, format = "%d/%m/%Y")
accidentes2020_fecha <- accidentes2020_data |> # Crea el dataset accidentes2020_fecha
select(fecha) |> # Selecciona la columna fecha
group_by(fecha) |> # Agrupa los datos por fecha
mutate(n = n()) |> # Crea una nueva columna n con el conteo de accidentes por fecha
unique() # Elimina duplicados, manteniendo una fila por fecha con el conteo3.4.2 Paso 2: Gráfico del cuándo según el día
ggplot(data = accidentes2020_fecha, # Crea un gráfico con el dataset accidentes2020_fecha
aes(x = fecha, y = n)) + # Define las estéticas del gráfico, con fecha en el eje x y n en el eje y
geom_line() + # Añade una línea para representar el número de accidentes por fecha
theme_minimal() # Aplica un tema minimalista al gráficoComo ya le advertimos al alcalde, el año 2020 no es bueno para sacar conclusiones. Ya le hemos pedido que nos facilite datos más amplios para poder proporcionarle una mejor visualización del número de accidentes por fecha, pero mientras tanto, nos tendremos que arreglar con lo que tenemos…
3.4.3 Gráfico del cuándo según la hora
# Convierte la columna hora a formato POSIXct y extrae la hora del día
accidentes2020_data$hora_dia <- hour(as.POSIXct(accidentes2020_data$hora, format = "%H:%M:%S"))
accidentes2020_hora_dia <- accidentes2020_data |> # Crea el dataset accidentes2020_hora_dia
select(hora_dia) |> # Selecciona la columna hora_dia
group_by(hora_dia) |> # Agrupa los datos por hora_dia
mutate(n = n()) |> # Crea una nueva columna n con el conteo de accidentes por hora
unique() # Elimina duplicados
ggplot(data = accidentes2020_hora_dia, # Crea un gráfico con el dataset accidentes2020_hora_dia
aes(x = hora_dia, y = n)) + # x= hora_dia e y=n
geom_line(linewidth = 1, color = "orange") + # Añade una línea de color naranja y ancho de línea 1
labs(title = "Número de accidentes según la franja horaria del día",
subtitle = "Las 14 y las 19 horas presentan el mayor número de accidentes",
x = "Hora del día",
y = "Número de accidentes en la franja horaria") +
scale_y_continuous(labels = function(x) { # Formatea las etiquetas del eje y
format(x,
big.mark = ".",
decimal.mark = ",",
scientific = FALSE # Usa puntos como separadores de miles y comas como separadores decimales
)
}) +
theme_minimal() Es evidente que la hora punta a mediodía y en la salida del trabajo, a las 14 y las 19 horas del día son los 2 picos a vigilar. Pero este gráfico tiene un gran margen de mejora.
3.5 El tipo de accidente
Ejercicio 3 ¿De qué tipo son los accidentes?
3.5.1 Paso 1: Resumen numérico por tipo de accidente
accidentes2020_data <- accidentes2020_data |>
mutate_if(is.character, as.factor)
accidentes2020_data |>
count(tipo_accidente) |>
mutate(porcentaje = 100 * n / sum(n)) tipo_accidente n porcentaje
1: Alcance 7294 22.4936010
2: Atropello a animal 75 0.2312887
3: Atropello a persona 2127 6.5593487
4: Caída 2118 6.5315940
5: Choque contra obstáculo fijo 4667 14.3923274
6: Colisión frontal 899 2.7723810
7: Colisión fronto-lateral 8081 24.9205909
8: Colisión lateral 4386 13.5257656
9: Colisión múltiple 2231 6.8800691
10: Despeñamiento 2 0.0061677
11: Otro 251 0.7740463
12: Solo salida de la vía 151 0.4656613
13: Vuelco 145 0.4471582
Las tablas también son elementos de visualización. En este caso, hay algo evidente que hacer, pero lo vamos a explicar con el siguiente gráfico:
3.5.2 Paso 2: Gráfico de barras por tipo de accidente
accidentes2020_data |>
ggplot() +
geom_bar(aes(y=tipo_accidente),
fill = "orange",
alpha = 0.6) +
theme_bw()¿Qué le falta a este gráfico?¿En qué se puede mejorar? Pues está muy claro: ordenar las categorías. Si no están ordenadas tardamos mucho en determinar, por ejemplo, cuál es la tercera, o la quinta… Únicamente la más importante es relativamente fácil de situar. Pero veamos cuando ordenamos las categorías:
3.5.3 Paso 3: Gráfico de barras por tipo de accidente ordenado
ggplot(accidentes2020_data, aes(y = reorder(tipo_accidente,
-tipo_accidente, # Reordena los tipos de accidente en orden descendente
length))) + # Usa la longitud para el reordenamiento
geom_bar(fill = "orange", alpha = 0.6) + # Añade barras al gráfico con color naranja y transparencia
labs(title = "Tipos de accidente en Madrid durante el año 2020",
subtitle = "Sobresalen la colisión fronto-lateral y el alcance",
x = "Número de accidentes",
y = "") +
scale_x_continuous(labels = function(x) { # Formatea las etiquetas del eje x
format(x,
big.mark = ".",
decimal.mark = ",",
scientific = FALSE
)
}) +
theme_bw() 3.6 Visualizaciones específicas georreferenciadas
Una categoría especial, y que merece mucho la pena destacar, cuando tenemos información georreferenciada, son los mapas interactivos. Una de las librerías más populares para construirlos es ‘leaflet’, que usa los mapas de OpenStreetMap, permitiendo incluir los mapas físicos de ESRI.
3.6.1 Paso 0: Preparación de los datos
# Transforma CRS de accidentes2020_sf a WGS 84 (CRS 4326)
accidentes2020_sf <- st_transform(accidentes2020_sf, crs = 4326)
# Extrae las coordenadas de longitud y las añade como una nueva columna en el dataframe
accidentes2020_data$longitude <- st_coordinates(accidentes2020_sf)[, 1]
accidentes2020_data$latitude <- st_coordinates(accidentes2020_sf)[, 2]
3.6.2 Paso 1: Primer mapa interactivo con leaflet
leaflet(data = accidentes2020_data) |> # Crea un mapa interactivo con el dataset accidentes2020_data
addTiles() |> # Añade las teselas (tiles) del mapa base
addCircleMarkers(lng = ~longitude, lat = ~latitude, # Añade marcadores circulares en lon y lat
radius = 1, # Define el radio,
color = "red", # Define el color
opacity = 0.5 # Define la opacidad de los marcadores
) # addProviderTiles(providers$Esri.WorldImagery)3.7 Piensa como un diseñador
Estas visualizaciones están muy bien pero… ¿y si pudiéramos aminorar la carga cognitiva del mapa interactivo? Una forma de hacerlo es usar agrupaciones. Veamos cómo hacerlo.
3.7.1 Paso 2: Primer mapa interactivo con leaflet y clusters
leaflet(data = accidentes2020_data) |>
addTiles() |>
addCircleMarkers(lng = ~longitude, lat = ~latitude,
radius = 2, color = "red",
opacity = 0.5,
clusterOptions = markerClusterOptions()
)Otra forma de visualizar que permite ‘leaflet’ son los mapas de calor:
3.7.2 Paso 3: Primer mapa interactivo con leaflet y mapa de calor
leaflet(data = accidentes2020_data) |>
addTiles() |>
addHeatmap(lng = ~longitude, lat = ~latitude,
blur = 50, max = 0.05)Ejercicio 4 Esta visualización la podemos configurar con los parámetros. Es tu turno: juega con ellos y consigue una gradación e intensidad de colores a tu gusto.
leaflet(data = accidentes2020_data) |>
addTiles() |>
addHeatmap(lng = ~longitude, lat = ~latitude,
blur = _____, max = _____)4 Contar una historia
Hemos visto el planteamiento, en el que proporcionamos el contexto: el mismísimo alcalde de Madrid se ha enterado de nuestras excelentes capacidades y nos ha pedido ayuda…
El análisis exploratorio nos ha ido introduciendo en la trama, donde descubrimos que hay una fuerte dependencia de la hora del día y del lugar donde se produce el accidente.
Pero como siempre, lo más importante es el desenlace, donde le estamos dando al alcalde las conclusiones más interesantes, que podríamos resumir en un par de gráficos:
Con el gráfico de evolución del número de accidentes a lo largo del día va a saber que (es lógico) que en las 14 y 19 horas se producen el mayor número de accidentes.
Con un mapa interactivo (podemos elegir cuál nos proporciona la mejor visualización) le damos la posibilidad de mirar de forma dinámica qué zonas son las que necesitan más acciones de protección.
4.1 Para pensar:
Nosotros te hemos propuesto un par de visualizaciones, pero puede que no estés de acuerdo… ¿Qué visualizaciones utilizarías en el desenlace, para aportarle el mayor conocimiento a Almeida sobre los accidentes de tráfico de su ciudad?